--these are functions for doing collision detection

--unless one wants to write their own routines, then the
-- collide_Object_bounds() and collide_Object_Rects() shouldn't be used
--
-- Instead, please use the check_collisions_*() functions


function collide_Object_bounds(a, b)
  --this is provided as a convenience so that one can make objects invisible to collisions
  if a.no_collide == true or b.no_collide == true then return false end

  local x1, x2, y1, y2, x3, x4, y3, y4 = a.x-a.x_offset, a.x + a.w-a.x_offset, a.y-a.y_offset, a.y + a.h-a.y_offset, b.x-b.x_offset, b.x + b.w-b.x_offset, b.y-b.y_offset, b.y + b.h-b.y_offset 
  
  --fully contained
  if ((x1 >= x3 and x2 <= x4) and (y1 >= y3 and y2 <= y4)) or
    ((x3 >= x1 and x4 <= x2) and (y3 >= y1 and y4 <= y2)) then
    return true
  end

  --top left corner
  if ((x1 >= x3 and x1 <= x4) and (y1 >= y3 and y1 <= y4)) or
    ((x3 >= x1 and x3 <= x2) and (y3 >= y1 and y3 <= y2)) then
    return true
  end

  --top right corner
  if ((x2 >= x3 and x2 <= x4) and (y1 >= y3 and y1 <= y4)) or
    ((x4 >= x1 and x4 <= x2) and (y3 >= y1 and y3 <= y2)) then
    return true
  end

  --bottom left corner
  if ((x1 >= x3 and x1 <= x4) and (y2 >= y3 and y2 <= y4)) or
    ((x3 >= x1 and x3 <= x2) and (y4 >= y1 and y4 <= y2)) then
    return true
  end

  --bottom right corner
  if ((x1 >= x3 and x1 <= x4) and (y2 >= y3 and y2 <= y4)) or
    ((x3 >= x1 and x3 <= x2) and (y4 >= y1 and y4 <= y2)) then
    return true
  end

  --top
  if ((x1 <= x3 and x2 >= x4) and (y1 >= y3 and y1 <= y4)) or
    ((x3 <= x1 and x4 >= x2) and (y3 >= y1 and y3 <= y2)) then
    return true
  end

  --left
  if ((x1 <= x3 and x1 >= x4) and (y1 >= y3 and y2 <= y4)) or
    ((x3 <= x1 and x3 >= x2) and (y3 >= y1 and y4 <= y2)) then
    return true
  end

  --right
  if ((x2 <= x3 and x2 >= x4) and (y1 >= y3 and y2 <= y4)) or
    ((x4 <= x1 and x4 >= x2) and (y3 >= y1 and y4 <= y2)) then
    return true
  end

  --bottom
  if ((x1 <= x3 and x2 >= x4) and (y2 >= y3 and y2 <= y4)) or
    ((x3 <= x1 and x4 >= x2) and (y4 >= y1 and y4 <= y2)) then
    return true
  end

  return false
end


function collide_Object_Rects(a, b)
  if a.no_collide or b.no_collide then return false end

  local a_ids = ObjectList:new()
  local b_ids = ObjectList:new()

  for r1 in a.rects:iterator() do
    for r2 in b.rects:iterator() do
      
      local flag = false

      local x1, x2, y1, y2, x3, x4, y3, y4 = r1.x+a.x-a.x_offset, r1.x+a.x+a.w-a.x_offset, r1.y+a.y-a.y_offset, r1.y+a.y+a.h-a.y_offset, r2.x+b.x-b.x_offset, r2.x+b.x+b.w-b.x_offset, r2.y+b.y-b.y_offset, r2.y+b.y+b.h-b.y_offset 
      
      --fully contained
      if ((x1 >= x3 and x2 <= x4) and (y1 >= y3 and y2 <= y4)) or
            ((x3 >= x1 and x4 <= x2) and (y3 >= y1 and y4 <= y2)) then
            flag = true
            if a_ids:exists(r1.id) == false then a_ids:push_back(r1.id) end
            if b_ids:exists(r2.id) == false then b_ids:push_back(r2.id) end
      end

      --top left corner
      if flag == false and ((x1 >= x3 and x1 <= x4) and (y1 >= y3 and y1 <= y4)) or
            ((x3 >= x1 and x3 <= x2) and (y3 >= y1 and y3 <= y2)) then
            flag = true
            if a_ids:exists(r1.id) == false then a_ids:push_back(r1.id) end
            if b_ids:exists(r2.id) == false then b_ids:push_back(r2.id) end
      end

      --top right corner
      if flag == false and ((x2 >= x3 and x2 <= x4) and (y1 >= y3 and y1 <= y4)) or
            ((x4 >= x1 and x4 <= x2) and (y3 >= y1 and y3 <= y2)) then
            flag = true
            if a_ids:exists(r1.id) == false then a_ids:push_back(r1.id) end
            if b_ids:exists(r2.id) == false then b_ids:push_back(r2.id) end
      end

      --bottom left corner
      if flag == false and ((x1 >= x3 and x1 <= x4) and (y2 >= y3 and y2 <= y4)) or
            ((x3 >= x1 and x3 <= x2) and (y4 >= y1 and y4 <= y2)) then
            flag = true
            if a_ids:exists(r1.id) == false then a_ids:push_back(r1.id) end
            if b_ids:exists(r2.id) == false then b_ids:push_back(r2.id) end
      end

      --bottom right corner
      if flag == false and ((x1 >= x3 and x1 <= x4) and (y2 >= y3 and y2 <= y4)) or
            ((x3 >= x1 and x3 <= x2) and (y4 >= y1 and y4 <= y2)) then
            flag = true
            if a_ids:exists(r1.id) == false then a_ids:push_back(r1.id) end
            if b_ids:exists(r2.id) == false then b_ids:push_back(r2.id) end
      end

      --top
      if flag == false and ((x1 <= x3 and x2 >= x4) and (y1 >= y3 and y1 <= y4)) or
            ((x3 <= x1 and x4 >= x2) and (y3 >= y1 and y3 <= y2)) then
            flag = true
            if a_ids:exists(r1.id) == false then a_ids:push_back(r1.id) end
            if b_ids:exists(r2.id) == false then b_ids:push_back(r2.id) end
      end

      --left
      if flag == false and ((x1 <= x3 and x1 >= x4) and (y1 >= y3 and y2 <= y4)) or
            ((x3 <= x1 and x3 >= x2) and (y3 >= y1 and y4 <= y2)) then
            flag = true
            if a_ids:exists(r1.id) == false then a_ids:push_back(r1.id) end
            if b_ids:exists(r2.id) == false then b_ids:push_back(r2.id) end
      end

      --right
      if flag == false and ((x2 <= x3 and x2 >= x4) and (y1 >= y3 and y2 <= y4)) or
            ((x4 <= x1 and x4 >= x2) and (y3 >= y1 and y4 <= y2)) then
            flag = true
            if a_ids:exists(r1.id) == false then a_ids:push_back(r1.id) end
            if b_ids:exists(r2.id) == false then b_ids:push_back(r2.id) end
      end

      --bottom
      if flag == false and ((x1 <= x3 and x2 >= x4) and (y2 >= y3 and y2 <= y4)) or
            ((x3 <= x1 and x4 >= x2) and (y4 >= y1 and y4 <= y2)) then
            flag = true
            if a_ids:exists(r1.id) == false then a_ids:push_back(r1.id) end
            if b_ids:exists(r2.id) == false then b_ids:push_back(r2.id) end
      end

    end
  end

  return a_ids, b_ids
end


--checks for collisions between two lists and calls the collide() function of each
function check_collisions_lists(list1, list2)
  local id1, id2
  for obj1 in list1:iterator() do
    for obj2 in list2:iterator() do
      if obj1 ~= obj2 and collide_Object_bounds(obj1, obj2) then
        id1, id2 = collide_Object_Rects(obj1, obj2)
        obj1:collide(id1, obj2)
        obj2:collide(id2, obj1)
      end
    end
  end
end

--checks for collisions between an object and a list
function check_collisions_obj_list(obj, list2)
  local id1, id2
  for obj2 in list2:iterator() do
    if obj1 ~= obj2 and collide_Object_bounds(obj, obj2) then
      id1, id2 = collide_Object_Rects(obj, obj2)
      obj:collide(id1, obj2)
      obj2:collide(id2, obj)
    end
  end
end

--checks for collisions between two objects
function check_collisions_obj_obj(obj1, obj2)
  local id1, id2
  if obj1 ~= obj2 and collide_Object_bounds(obj1, obj2) then
    id1, id2 = collide_Object_Rects(obj1, obj2)
    obj1:collide(id1, obj2)
    obj2:collide(id2, obj1)
  end
end

--checks for collisions between an object and a list. returns true or false.
function check_collisions_any(obj, list)
  local id1, id2
  for obj2 in list2:iterator() do
    if collide_Object_bounds(obj, obj2) then
      id1, id2 = collide_Object_Rects(obj, obj2)
      if id1.size > 0 and id2.size > 0 then
        return true
      else
        return false
      end
    end
  end
end
